package xfix;

import java.util.*;

public class XbiSearch
{
	private List<RootCauseList> chromosome;
	private List<RootCauseList> initialChromosome;
	private List<RootCauseList> optimalChromosome;

	private String optimalValue;
	private double optimalFitnessScore;

	private Map<String, List<OptimalRootCause>> optimalMap;	// <xpath, <prop, val, fitnessScore>>

	private static double phase1TimeInSec;
	private static double phase2TimeInSec;
	private Map<String,Set<String>> historyProps;

	public XbiSearch(List<RootCauseList> chromosome)
	{
		this.chromosome =  chromosome;
		this.initialChromosome = chromosome;
		this.optimalMap = new HashMap<String, List<OptimalRootCause>>();
		this.optimalChromosome = chromosome;
		this.historyProps = new HashMap<>();
	}

	public List<RootCauseList> getChromosome() {
		return chromosome;
	}

	public void setChromosome(List<RootCauseList> chromosome) {
		this.chromosome = chromosome;
	}

	public List<RootCauseList> getInitialChromosome() {
		return initialChromosome;
	}

	public void setInitialChromosome(List<RootCauseList> initialChromosome) {
		this.initialChromosome = initialChromosome;
	}

	public List<RootCauseList> getOptimalChromosome() {
		return optimalChromosome;
	}

	public void setOptimalChromosome(List<RootCauseList> optimalChromosome) {
		this.optimalChromosome = optimalChromosome;
	}

	public String getOptimalValue()
	{
		return optimalValue;
	}

	public void setOptimalValue(String optimalValue)
	{
		this.optimalValue = optimalValue;
	}

	public double getOptimalFitnessScore()
	{
		return optimalFitnessScore;
	}

	public void setOptimalFitnessScore(double optimalFitnessScore)
	{
		this.optimalFitnessScore = optimalFitnessScore;
	}

	public Map<String, List<OptimalRootCause>> getOptimalMap()
	{
		return optimalMap;
	}

	public void setOptimalMap(Map<String, List<OptimalRootCause>> optimalMap)
	{
		this.optimalMap = optimalMap;
	}
	public static double getPhase1TimeInSec()
	{
		return phase1TimeInSec;
	}
	public static double getPhase2TimeInSec()
	{
		return phase2TimeInSec;
	}
	public static void setPhase1TimeInSec(double phase1TimeInSec) {
		XbiSearch.phase1TimeInSec = phase1TimeInSec;
	}

	public static void setPhase2TimeInSec(double phase2TimeInSec) {
		XbiSearch.phase2TimeInSec = phase2TimeInSec;
	}

	public boolean search()
	{
		long startTime,endTime;
		optimalFitnessScore = initialChromosome.get(0).getGlobalFitnessScore();  //initial XBI个数
		for (RootCauseList rootCauseList : initialChromosome) {
			startTime = System.nanoTime();

			processRootCauses(rootCauseList);
			if(optimalFitnessScore == Constants.OPTIMAL_FITNESS_SCORE){
				break;
			}
			endTime = System.nanoTime();
			phase1TimeInSec = phase1TimeInSec + Util.convertNanosecondsToSeconds((endTime - startTime));
		}
		startTime = System.nanoTime();
		boolean res = runBestRootCauseCombination(optimalFitnessScore);
		endTime = System.nanoTime();
		phase2TimeInSec = phase2TimeInSec + Util.convertNanosecondsToSeconds((endTime - startTime));

		System.out.println("\nTotal gene processing (phase 1) time = " + phase1TimeInSec + " sec");
		System.out.println("\nTotal best root cause computation (phase 2) time = " + phase2TimeInSec + " sec");
		System.out.println("\n------------------------------------------------------");
		System.out.println("Computing fitness function of optimal chromosome");
		/*XbiFitnessFunction fitnessFunction = new XbiFitnessFunction();
		fitnessFunction.getFitnessScoreGlobal(optimalChromosome);*/
		System.out.println("Global fitness score = " + optimalChromosome.get(0).getGlobalFitnessScore());
		System.out.println("------------------------------------------------------");
		System.out.println("\noptimal map = " + optimalMap);
		System.out.println("\noptimal chromosome = " + optimalChromosome);
		return res;
	}

	public boolean runBestRootCauseCombination(double optimalFitnessScore)	{
		Iterator<String> iter = optimalMap.keySet().iterator();
		List<OptimalRootCause> bestGenes = new ArrayList<>();
		double bestGlobalScore = optimalFitnessScore;
		double[] compare = new double[]{0.9,0.9,0.9,0.9};
		while (iter.hasNext()) {
			String key = iter.next();
			OptimalRootCause gene = null;
			double bestXBI = optimalFitnessScore;
			List<OptimalRootCause> value = optimalMap.get(key);
			for (OptimalRootCause rootCause:value){
				if(Constants.EXPLORATORY_ENUM_CSS_SET.contains(rootCause.getProp())){
					if(rootCause.isImprovement() && rootCause.getGlobalFitnessScore() < bestXBI
							&& rootCause.getPartXbis().size() <=1){
						bestXBI = rootCause.getGlobalFitnessScore();
						bestGlobalScore = Math.min(bestGlobalScore,bestXBI);
						gene = rootCause;
					}
				}else if(rootCause.isImprovement() && rootCause.getGlobalFitnessScore() < bestXBI
						&& rootCause.getPartXbis().size() <=1 && Util.doubleArrayAbsAllLess(rootCause.getLocalFitnessScore(),compare)){
					bestXBI = rootCause.getGlobalFitnessScore();
					bestGlobalScore = Math.min(bestGlobalScore,bestXBI);
					gene = rootCause;
				}
			}
			if(gene != null){
				bestGenes.add(gene);
			}
		}
		for (RootCauseList rootCauseList : optimalChromosome){
//			rootCauseList.setGlobalFitnessScore(bestXBI);
			for (OptimalRootCause bestGene:bestGenes){
				if(rootCauseList.getGene(bestGene.getXpath()) != null){
					RootCause gene = rootCauseList.getGene(bestGene.getXpath());
					gene.updateValue(bestGene.getProp(), bestGene.getVal());
				}
			}

		}
		if(bestGenes.size() != 0){
			return true;
		}else{
			return false;
		}
	}


	//处理一个块内的XBI，为0后直接break
	public void processRootCauses(RootCauseList rootCauseList)
	{
		int geneCount = 0;
		//指的是所有XBI
		optimalFitnessScore = rootCauseList.getGlobalFitnessScore();

		for(RootCause gene : rootCauseList.getGenes())
		{
			geneCount++;
			RootCauseList tempRootCauseList = rootCauseList.copy();
			RootCause tempGene = tempRootCauseList.getGene(gene.getXpath());

			// check if optimalMap contains sibling of the gene
//			boolean siblingFound = false;
			System.out.println("Now processing gene " + gene.getXpath());
			EvaluateRootCause evaluateGene = new EvaluateRootCause(rootCauseList, tempRootCauseList,
					rootCauseList.getXpertXbi(),historyProps);
			evaluateGene.runGeneSearch(tempGene, geneCount);
			Set<String> keySet = evaluateGene.getOptimalMap().keySet();
			for (String key : keySet){
				List<OptimalRootCause> oGeneList = evaluateGene.getOptimalMap().get(key);
				if(optimalMap.containsKey(key)){
					List<OptimalRootCause> optimalRootCauses = optimalMap.get(gene.getXpath());
					optimalRootCauses.addAll(oGeneList);
					optimalMap.put(gene.getXpath(), optimalRootCauses);
				}else {
					optimalMap.put(gene.getXpath(), oGeneList);
				}
			}
//			optimalMap.putAll(evaluateGene.getOptimalMap());
			//XBI全部消失
			if(optimalFitnessScore == Constants.OPTIMAL_FITNESS_SCORE)
			{
				break;
			}
		}
	}
}